﻿//===============================================================================
// Microsoft patterns & practices
// Windows Phone 7 Developer Guide
//===============================================================================
// Copyright © Microsoft Corporation.  All rights reserved.
// This code released under the terms of the 
// Microsoft patterns & practices license (http://wp7guide.codeplex.com/license)
//===============================================================================


namespace AzureToolkit
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Text;
    using Microsoft.WindowsAzure;
    using Microsoft.WindowsAzure.StorageClient;
    using System.Diagnostics.Contracts;

    public class EntitiesBlobContainer<T> : IEntitiesBlobContainer<T>, IInitializer
    {
        private readonly CloudStorageAccount account;
        private readonly CloudBlobContainer container;
        private readonly BlobContainerPermissions blobContainerPermissions;

        public EntitiesBlobContainer()
            : this(CloudConfiguration.GetStorageAccount())
        {
        }

        public EntitiesBlobContainer(CloudStorageAccount account)
            : this(account, (string)null)
        {
        }

        public EntitiesBlobContainer(CloudStorageAccount account, BlobContainerPermissions blobContainerPermissions)
            : this(account, null, blobContainerPermissions)
        {
        }

        public EntitiesBlobContainer(CloudStorageAccount account, string conatinerName)
            : this(account, conatinerName, new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Off })
        {
        }

        public EntitiesBlobContainer(CloudStorageAccount account, string containerName, BlobContainerPermissions blobContainerPermissions)
        {
            Contract.Requires(account != null);

            this.account = account;

            if (String.IsNullOrEmpty(containerName))
            {
                var type = typeof(T);
                if (!type.IsClass)
                {
                    throw new ArgumentNullException("You must specify a container for containers that use value types.");
                }
                containerName = type.Name.ToLowerInvariant();
                if ((containerName.EndsWith("blob") || containerName.EndsWith("view")) && containerName.Length > 4)
                {
                    containerName = containerName.Substring(0, containerName.Length - 4);
                }
            }

            var client = this.account.CreateCloudBlobClient();
            if (client == null)
            {
                throw new InvalidOperationException("Client is null.");
            }

            client.RetryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(5));

            this.blobContainerPermissions = blobContainerPermissions;
            this.container = client.GetContainerReference(containerName.ToLowerInvariant());
        }

        public void Initialize()
        {
            if (this.container.CreateIfNotExist())
            {
                this.container.SetPermissions(this.blobContainerPermissions);
            }
        }

        public void Save(string objId, T obj)
        {
            CloudBlob blob = this.container.GetBlobReference(String.Concat(objId, ".json"));
            blob.Properties.ContentType = "application/json";
            blob.UploadText(JsonSerializer.Current.SerializeObject(obj));
        }

        public void SaveAsync(string objId, T obj)
        {
            CloudBlob blob = this.container.GetBlobReference(String.Concat(objId, ".json"));
            blob.Properties.ContentType = "application/json";
            string json = JsonSerializer.Current.SerializeObject(obj);
            byte[] bytes = Encoding.UTF8.GetBytes(json);
            MemoryStream stream = new MemoryStream(bytes);
            blob.BeginUploadFromStream(stream, (ar) =>
            {
                var resultBlob = ar.AsyncState as CloudBlob;
                resultBlob.EndUploadFromStream(ar);
            }, blob);
        }

        public T Get(string objId)
        {
            CloudBlob blob = this.container.GetBlobReference(String.Concat(objId, ".json"));
            try
            {
                return JsonSerializer.Current.DeserializeObject<T>(blob.DownloadText());
            }
            catch (StorageClientException)
            {
                return default(T);
            }
        }

        public string GetAsJson(string objId)
        {
            CloudBlob blob = this.container.GetBlobReference(String.Concat(objId, ".json"));
            return blob.DownloadText();
        }

        public IEnumerable<T> GetAll()
        {
            IEnumerable<IListBlobItem> blobItems = this.container.ListBlobs();

            return blobItems.Select(blobItem => this.Get(blobItem.Uri.ToString())).ToList();
        }

        public Uri GetUri(string objId)
        {
            return new Uri(String.Concat(objId, ".json"));
        }

        public void Delete(string objId)
        {
            CloudBlob blob = this.container.GetBlobReference(String.Concat(objId, ".json"));
            blob.DeleteIfExists();
        }

        public void DeleteContainer()
        {
            try
            {
                this.container.Delete();
            }
            catch (StorageClientException ex)
            {
                if (ex.StatusCode == HttpStatusCode.NotFound)
                {
                    return;
                }

                throw;
            }
        }
    }
}